#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import time
import configparser
import multiprocessing
from os.path import join
from pprint import pprint
from vmware import VM
import vmdisk as vmdisk_lib
from util import file_exists
from vm_monitoring.settings import BASE_CONF_DIR
from vsphere.models import VMInfo, VMSumInfo, VMDiskInfo
from vsphere.utils.constants import VM_INFO_SAMPLE

_VMS_OBJ = None
_TIME_INTERVAL = 300


def vcenter_conf(conf):
    """Parse the vcenter configuration file"""
    if file_exists(conf):
        parser = configparser.SafeConfigParser()
        with open(conf, 'r') as f:
            parser.readfp(f)

        def get(parser, name):
            """Parse key-value from the conf file"""
            if parser.has_option('vcenter', name):
                return parser.get('vcenter', name)
            else:
                raise Exception(
                    'Unable to get value of \'%s\' on vcenter part' % name)

        return (get(parser, 'vcenter'), get(parser, 'user'),
                get(parser, 'password'))

    else:
        raise Exception('File %s doesn\'t exits' % conf)


def get_global_vms_obj(conf=None, refresh=False):
    global _VMS_OBJ
    if _VMS_OBJ is None or refresh:
        if conf is None:
            conf = join(BASE_CONF_DIR, 'vm.conf')
        vcenter, user, password = vcenter_conf(conf)
        _VMS_OBJ = VM(vcenter, user, password)

    return _VMS_OBJ


def get_vms_info(conf=None, filter_key='name', reverse=False, refresh=False):
    """
        Supported filter_key:
            name
            state
            ip
            hostname
            version
    """
    vms_obj = get_global_vms_obj(conf, refresh)
    if filter_key is not None:
        try:
            vms_obj.all_vms.sort(
                key=lambda x: x[filter_key], reverse=reverse)
        except KeyError:
            raise Exception(
                'VM key \'%s\' doesn\'t exits' % filter_key)

    return {
        'total_vm_number': vms_obj.total_vm_number,
        'total_disk_capacity':
            vms_obj.total_disk_capacity,
        'total_thin_disk_capacity':
            vms_obj.total_thin_disk_capacity,
        'total_thick_disk_capacity':
            vms_obj.total_thick_disk_capacity,
        'total_datastore_capacity':
            vms_obj.total_datastore_capacity,
        'total_left_datastore_capacity':
            vms_obj.total_left_datastore_capacity,
        'total_provision_datastore_capacity':
            vms_obj.total_provision_datastore_capacity,
        'vms': vms_obj.all_vms
    }


def delete_vm(uuid, conf=None, refresh=False):
    vms_obj = get_global_vms_obj(conf, refresh)
    if isinstance(uuid, (list, tuple)):
        for u in uuid:
            vms_obj.delete_vm_by_uuid(u)
    else:
        vms_obj.delete_vm_by_uuid(uuid)


def poweroff_vm(uuid, conf=None, refresh=False):
    vms_obj = get_global_vms_obj(conf, refresh)
    if isinstance(uuid, (list, tuple)):
        for u in uuid:
            vms_obj.poweroff_vm_by_uuid(u)
    else:
        vms_obj.poweroff_vm_by_uuid(uuid)


def power_action_vm(uuid, action, conf=None, refresh=False):
    vms_obj = get_global_vms_obj(conf, refresh)
    vms_obj.change_power_status_by_uuid(uuid, action)
    action = action[5:].capitalize()
    if hasattr(uuid, '__iter__'):
        for u in uuid:
            VMInfo.objects.filter(uuid=u).update(state=action)
    else:
        VMInfo.objects.filter(uuid=uuid).update(state=action)


def deploy_vm(deploy_request):
    vcenter, user, password = vcenter_conf(join(BASE_CONF_DIR, 'vm.conf'))
    count = deploy_request.get_deploy_count()
    for i in range(count):
        vms_obj = get_global_vms_obj()
        vms_obj.deploy(deploy_request.get_deploy_request(i), vcenter, user, password)
    return count


def update_vminfo():
    print '[pid=%d] Updating vm data ...' % os.getpid()
    obj = get_vms_info(refresh=True)
    # obj = VM_INFO_SAMPLE
    if len(VMInfo.objects.all()):
        VMInfo.objects.all().delete()
    if len(VMDiskInfo.objects.all()):
        VMDiskInfo.objects.all().delete()
    if len(VMSumInfo.objects.all()):
        VMSumInfo.objects.all().delete()

    for vm in obj['vms']:
        pv = VMInfo(name=vm['name'],
                    ip=vm['ip'],
                    hostname=vm['hostname'],
                    uuid=vm['uuid'],
                    version=vm['version'],
                    memoryMB=vm['memoryMB'],
                    numCPU=vm['numCPU'],
                    state=vm['state'],
                    total_capacity=vm['total_capacity'],
                    total_capacity_in_kb=vm['total_capacity_in_kb'])
        pv.save()
        for base_disk in vm['base_disks']:
            pd = VMDiskInfo(vm_uuid=pv,
                            uuid=base_disk['uuid'],
                            wwid=base_disk['wwid'],
                            size=base_disk['size'],
                            label=base_disk['label'],
                            diskMode=base_disk['diskMode'],
                            unitNumber=base_disk['unitNumber'],
                            thinProvisioned=base_disk['thinProvisioned'],
                            capacityInKB=base_disk['capacityInKB'])

            pd.save()

    p = VMSumInfo(total_vm_number=obj['total_vm_number'],
                  total_datastore_capacity=obj[
                    'total_datastore_capacity'],
                  total_left_datastore_capacity=obj[
                    'total_left_datastore_capacity'],
                  total_provision_datastore_capacity=obj[
                    'total_provision_datastore_capacity'])
    p.save()


class UpdateVmInfo(multiprocessing.Process):
    def __init__(self, interval):
        super(self.__class__, self).__init__()
        self.interval = interval

    def run(self):
        while True:
            print "[pid=%d] sleep for next ..." % os.getpid()
            time.sleep(self.interval)
            update_vminfo()


def init_vminfo(debug=False):
    reload(sys)
    sys.setdefaultencoding("utf-8")
    print '[Fpid=%d] Start to initialize vm_info data ...' % os.getpid()
    update_vminfo()

    if not debug:
        p = UpdateVmInfo(_TIME_INTERVAL)
        p.start()


def add_disk_to_vm(uuid, scsi_number,
                   size_gb=500,
                   provision_type='thin',
                   mode='independent_persistent'):
    """
    Add disk api for automation cases
    Args:
        virtual_disk: A virtual disk
        scsi_number: SCSI number string
        disk_size_gb: size of the disk to be added
        disk_provision_type: Disk provision type when adding disk for VMWare.
        disk_mode: Disk mode when adding disk for VMWare

    Returns:Boolean

    """
    vm_disk = vmdisk_lib.VirtualDisk(uuid, disk_size_gb=size_gb,
                                     disk_provision_type=provision_type,
                                     disk_mode=mode)
    scsi_num_tuple = vmdisk_lib.get_scsi_num_tuple(scsi_number)
    if vm_disk.add_virtual_disk(scsi_num_tuple):
        return True
    return False


def remove_disk_from_vm(uuid, scsi_number):
    """
    Remove disk api for automation cases
    Args:
        virtual_disk: A virtual disk
        scsi_number: SCSI number string

    Returns:Boolean

    """
    scsi_num_tuple = vmdisk_lib.get_scsi_num_tuple(scsi_number)
    vm_disk = vmdisk_lib.VirtualDisk(uuid)
    print scsi_num_tuple
    if vm_disk.remove_virtual_disk(scsi_num_tuple):
        return True
    return False


def remove_scsi_controller_from_vm(uuid, number):
    """
    Remove SCSI controller api for automation cases
    Args:
        virtual_disk: A virtual disk
        number: SCSI controller number

    Returns:Boolean

    """
    number = int(number)
    assert vmdisk_lib.is_valid_scsi(number)
    vm_disk = vmdisk_lib.VirtualDisk(uuid)
    if vm_disk.remove_scsi_controller(number):
        return True
    return False


def add_scsi_controller_to_vm(uuid, number):
    """
    Add SCSI controller api for automation cases
    Args:
        virtual_disk: A virtual disk
        number: SCSI controller number

    Returns:Boolean

    """
    number = int(number)
    assert vmdisk_lib.is_valid_scsi(number)
    vm_disk = vmdisk_lib.VirtualDisk(uuid)
    if vm_disk.add_scsi_controller(number):
        return True
    return False


# if __name__ == '__main__':
#     o = get_vms_info(filter_key='ip')
#     pprint(o)
#     uuid = ['4204e88a-3d8c-4684-c33f-9abcc51b59bc']
#     power_action_vm(uuid, 'poweron')